
#include <as.h>
#include "runtime_types.h"

#include "proc.h"
#include "lock.h"
#include "util.h"
#include "panic.h"

#include "mem_fixalloc.h"
#include "mem_sys.h"
#include "mem_heap.h"
#include "mem_size.h"
#include "mem_cache.h"
#include "mem_stats.h"
#include "mem_gc.h"
#include "mem_alloc.h"

static void
scanstack(void (*scanblock)(byte*, uintptr), G *gp)
{
	// TODO: Support split stacks sometime.
	byte* bottom;
	byte* top;

	if (gp == runtime_g()) {
		// Scanning our own stack.
		top = (byte*)&gp;
	} else {
		// Scanning another goroutine's stack.
		// The goroutine is usually asleep (the world is stopped).
		top = (byte*)runtime_gstack_last(gp);
		if (top == NULL) {
			runtime_throw("something's wrong in scanstack()");
		}
	}
	bottom = (byte*)runtime_gstack_start(gp);
	if (bottom == NULL) {
		runtime_throw("something's wrong in scanstack()");
	}

	if(top > bottom) {
		scanblock(bottom, top - bottom);
	} else {
		scanblock(top, bottom - top);
	}
}

void runtime_scan_stacks(void (*scanner)(byte*, uintptr))
{
	for (G *g = runtime_allg(); g != NULL; g = g->all_link) {
		if (g->status != Gdead) {
			scanstack(scanner, g);
		}
	}
}

static struct root_list *roots;

void
__go_register_gc_roots (struct root_list* r)
{
	r->next = roots;
	roots = r;
}

void runtime_scan_roots(void (*scanner)(byte*, uintptr))
{
	struct root_list *r;

	for (r = roots; r != NULL; r = r->next) {
		for (int i = 0; r->roots[i].decl != NULL; i++) {
			scanner(r->roots[i].decl, r->roots[i].size);
		}
	}
}

void
runtime_stealcache(void)
{
	// TODO: support multiple threads

	runtime_MCache_ReleaseAll(runtime_mcache());
}

void
runtime_purgecachedstats(void)
{
	MCache *c = runtime_mcache();

	// Protected by either heap or GC lock.
	mstats.heap_alloc += c->local_cachealloc;
	c->local_cachealloc = 0;
	mstats.heap_objects += c->local_objects;
	c->local_objects = 0;
	mstats.nmalloc += c->local_nmalloc;
	c->local_nmalloc = 0;
	mstats.nfree += c->local_nfree;
	c->local_nfree = 0;
	mstats.nlookup += c->local_nlookup;
	c->local_nlookup = 0;
	mstats.alloc += c->local_alloc;
	c->local_alloc= 0;
	mstats.total_alloc += c->local_total_alloc;
	c->local_total_alloc= 0;
}

void
runtime_flushstats(void)
{
	// TODO: support multiple threads

	runtime_purgecachedstats();

	MCache *c = runtime_mcache();
	for(uintptr i = 0; i < nelem(c->local_by_size); i++) {
		mstats.by_size[i].nmalloc += c->local_by_size[i].nmalloc;
		c->local_by_size[i].nmalloc = 0;
		mstats.by_size[i].nfree += c->local_by_size[i].nfree;
		c->local_by_size[i].nfree = 0;
	}
}

void runtime_ReadMemStats(MStats *)
  __asm__("runtime.ReadMemStats");

void
runtime_ReadMemStats(MStats *stats)
{
	// TODO: thread safety

	runtime_flushstats();
	*stats = mstats;
}
